import { PIDController } from "../core/utils";

const { executeInEditMode, ccclass, property } = cc._decorator;

@ccclass
@executeInEditMode
export default class ReactNode extends cc.Component {
    @property(cc.Node)
    target: cc.Node = null;

    @property
    effectSize = false;
    @property({
        visible() {
            return this.effectSize;
        }
    })
    offsetSize: cc.Size = new cc.Size(0, 0);

    @property
    effectActive = true;

    @property
    effectPos = false;
    @property({
        visible() {
            return this.effectPos;
        }
    })
    offsetPos: cc.Vec2 = new cc.Vec2();

    @property
    effectAngle = false;
    @property({
        visible() {
            return this.effectAngle;
        }
    })
    offsetAngle: number = 0


    @property({
        visible: function () {
            return this.effectPos;
        }
    })
    edge = false;

    @property({
        visible: function () {
            return this.edge && this.effectPos;
        }
    })
    top: number = 0;
    @property({
        visible: function () {
            return this.edge && this.effectPos;
        }
    })
    right: number = 0;
    @property({
        visible: function () {
            return this.edge && this.effectPos;
        }
    })
    bottom: number = 0;
    @property({
        visible: function () {
            return this.edge && this.effectPos;
        }
    })
    left: number = 0;

    @property
    effectScale = false;
    @property({
        visible: function () {
            return this.effectScale;
        }
    })
    offsetScale: cc.Vec2 = cc.v2(1, 1);

    @property
    effectOpacity = false;
    @property({
        visible: function () {
            return this.effectOpacity;
        }
    })
    offsetOpacity: number = 0;

    @property
    reactInUpdate = true;

    @property({
        serializable: true,
        visible: false
    })
    _world = false;
    @property
    get world() {
        return this._world;
    }
    set world(val) {
        this._world = val;
        this.onChanged();
    }
    @property({
        serializable: true
    })
    _lazy = false;
    @property
    get lazy() {
        return this._lazy;
    }
    set lazy(val) {
        this._lazy = val;
        this.onChanged();
    }
    pidX: PIDController = new PIDController();
    pidY: PIDController = new PIDController();
    protected _speedV2: cc.Vec2 = cc.v2();

    setLazyPID(Kp = 0.15, Ki = 0.15, Kd = 0.04) {
        this.pidX.reset(Kp, Ki, Kd);
        this.pidY.reset(Kp, Ki, Kd);
    }

    onLoad() {
        if (!cc.isValid(this.target)) {
            return;
        }

        this.target.on("size-changed", this.onChanged, this);
        this.target.on("position-changed", this.onChanged, this);
        this.target.on("scale-changed", this.onChanged, this);
        this.target.on("opacity-changed", this.onChanged, this);
        this.target.on("active-in-hierarchy-changed", this.onChanged, this);
        this.onChanged();
    }
    setTarget(target: cc.Node) {
        if (cc.isValid(this.target)) {
            this.target.off("size-changed", this.onChanged, this);
            this.target.off("position-changed", this.onChanged, this);
            this.target.off("scale-changed", this.onChanged, this);
            this.target.off("opacity-changed", this.onChanged, this);
            this.target.off("active-in-hierarchy-changed", this.onChanged, this);
        }

        this.target = target;
        if (cc.isValid(this.target)) {
            this.target.on("size-changed", this.onChanged, this);
            this.target.on("position-changed", this.onChanged, this);
            this.target.on("scale-changed", this.onChanged, this);
            this.target.on("opacity-changed", this.onChanged, this);
            this.target.on("active-in-hierarchy-changed", this.onChanged, this);
            this.onChanged();
        }
    }
    onChanged() {
        if (!cc.isValid(this.target)) {
            return;
        }

        if (this.effectActive) {
            this.node.active = this.target.activeInHierarchy;
        }

        if (!this.node.active) {
            return;
        }
        if (this.effectAngle) {
            this.node.angle = this.target.angle + this.offsetAngle;
        }
        if (this.effectSize) {
            this.node.width = this.target.width + this.offsetSize.width;
            this.node.height = this.target.height + this.offsetSize.height;
        }
        if (this.effectScale) {
            this.node.scaleX = this.target.scaleX * this.offsetScale.x;
            this.node.scaleY = this.target.scaleY * this.offsetScale.y;
        }
        if (this.effectOpacity) {
            this.node.opacity = this.target.opacity + this.offsetOpacity;
        }
        if (this.effectPos) {
            let pos = null;
            if (this._world) {
                pos = this.target.convertToWorldSpaceAR(cc.Vec2.ZERO);
                pos = this.node.parent.convertToNodeSpaceAR(pos);
            }
            else {
                pos = this.target.position;
            }

            pos = pos.add(this.offsetPos.mul(this.target.scale));
            if (this.edge) {

                if (pos.x - this.node.width * this.node.anchorX < this.left) {
                    pos.x = this.left + this.node.width * this.node.anchorX;
                }
                else if (pos.x + this.node.width * (1 - this.node.anchorX) > this.right) {
                    pos.x = this.right - this.node.width * (1 - this.node.anchorX);
                }

                if (pos.y - this.node.height * this.node.anchorY < this.bottom) {
                    pos.y = this.bottom + this.node.height * this.node.anchorY;
                }
                else if (pos.y + this.node.height * (1 - this.node.anchorY) > this.top) {
                    pos.y = this.top - this.node.height * (1 - this.node.anchorY);
                }
            }

            if (this.lazy) {
                this.pidX.setTarget(pos.x);
                this.pidY.setTarget(pos.y);
            }
            else {
                this.node.setPosition(pos);
            }

        }
    }

    followPosition(dt: number) {
        if (!cc.isValid(this.target)) {
            return;
        }
        if (this.effectPos) {
            this.node.setPosition(
                this.pidX.update(this.node.x),
                this.pidY.update(this.node.y),
            );
        }
    }
    protected lateUpdate(dt: number): void {
        if (CC_EDITOR || this.reactInUpdate) {
            this.onChanged();
        }
        if (this.lazy) {
            this.followPosition(dt);
        }
    }
}
